#ifndef __MCS__VAR_ARG_FUNCTION_COLLECTION__HPP
#define __MCS__VAR_ARG_FUNCTION_COLLECTION__HPP

#include "FunctionFactory.hpp"

/// Collection of functions for generating argument lists
class VarArgFunctionCollection : public FunctionCollection
{
public:
	virtual void registerFunctions(FunctionFactory * factory)
	{
		factory->insertFunction("subst", &functionSubst<bool>);
		factory->insertFunction("subst", &functionSubst<int>);
		factory->insertFunction("subst", &functionSubst<double>);
		factory->insertFunction("subst", &functionSubst<String const &>);
		factory->insertFunction("iter", &functionIterate0);
		factory->insertFunction("iter", &functionIterate1);
		factory->insertFunction("list", &functionList0);
		factory->insertFunction("list", &functionList1);
		factory->insertFunction("list", &functionList2);
		factory->insertFunction("list", &functionList3);
		factory->insertFunction("listx", &functionListX0);
		factory->insertFunction("listx", &functionListX1);
		factory->insertFunction("listx", &functionListX2);
		factory->insertFunction("listx", &functionListX3);
		factory->insertFunction("classArgs", &functionClassArgs0);
		factory->insertFunction("classArgs", &functionClassArgs1);
		factory->insertFunction("classArgs", &functionClassArgs2);
		factory->insertFunction("classArgs", &functionClassArgs3);
		factory->insertFunction("classArgs", &functionClassArgs4);
		factory->insertFunction("classArgs", &functionClassArgs5);
		factory->insertFunction("classArgs", &functionClassArgs6);
		factory->insertFunction("classArgs", &functionClassArgs7);
		factory->insertFunction("selArgs", &functionSelArgs0);
		factory->insertFunction("selArgs", &functionSelArgs1);
		factory->insertFunction("selArgs", &functionSelArgs2);
		factory->insertFunction("selArgs", &functionSelArgs3);
		factory->insertFunction("selArgs", &functionSelArgs4);
		factory->insertFunction("selArgs", &functionSelArgs5);
		factory->insertFunction("selArgs", &functionSelArgs6);
		factory->insertFunction("selArgs", &functionSelArgs7);
		factory->insertFunction("funcArgs", &functionFuncArgs0);
		factory->insertFunction("funcArgs", &functionFuncArgs1);
		factory->insertFunction("funcArgs", &functionFuncArgs2);
		factory->insertFunction("funcArgs", &functionFuncArgs3);
		factory->insertFunction("funcArgs", &functionFuncArgs4);
		factory->insertFunction("funcArgs", &functionFuncArgs5);
		factory->insertFunction("funcArgs", &functionFuncArgs6);
		factory->insertFunction("funcArgs", &functionFuncArgs7);
		factory->insertFunction("callArgs", &functionCallArgs0);
		factory->insertFunction("callArgs", &functionCallArgs1);
		factory->insertFunction("callArgs", &functionCallArgs2);
		factory->insertFunction("callArgs", &functionCallArgs3);
		factory->insertFunction("callArgs", &functionCallArgs4);
		factory->insertFunction("callArgs", &functionCallArgs5);
		factory->insertFunction("callArgs", &functionCallArgs6);
		factory->insertFunction("callArgs", &functionCallArgs7);
	}
private:
	template<class T> static String functionSubst(String const & templ, T val)
	{
		std::stringstream str;
		bool hasPercent = false;
		static char const percentChar = '%';

		for(size_t j=0; j<templ.size(); ++j)
		{
			char c = templ.at(j);
			bool isPercent = (c == percentChar);

			if(hasPercent)
			{
				if(isPercent) str << percentChar; else str << val;
				hasPercent = false;
			}
			else
			{
				if(isPercent) hasPercent = true;
			}

			if(!isPercent) str << c;
		}
		if(hasPercent)
		{
			str << val;
			hasPercent = false;
		}
		return str.str();
	}

	static String functionIterate0(String const & templ, int base, int count)
	{
		std::stringstream str;
		if(count < 0)
		{
			for(int i=base; count < 0; --i, ++count )
			{
				str << functionSubst(templ, i);
			}
		}
		else
		{
			for(int i=base; count > 0; ++i, --count )
			{
				str << functionSubst(templ, i);
			}
		}
		return str.str();
	}

	static String functionIterate1(String const & templ, int count) { return functionIterate0(templ, 0, count); }

	static String functionList0(String const & templ, int base, int count, String const & divisor)
	{
		std::stringstream str;
		if(count < 0)
		{
			for(int i=base; count < 0; --i, ++count )
			{
				if(i != base) str << divisor;
				str << functionSubst(templ, i);
			}
		}
		else
		{
			for(int i=base; count > 0; ++i, --count )
			{
				if(i != base) str << divisor;
				str << functionSubst(templ, i);
			}
		}
		return str.str();
	}

	static String functionList1(String const & templ, int count, String const & divisor) { return functionList0(templ, 0, count, divisor); }
	static String functionList2(String const & templ, int base, int count) { return functionList0(templ, base, count, ", "); }
	static String functionList3(String const & templ, int count) { return functionList2(templ, 0, count); }

	static String functionListX0(String const & wrapping, String const & templ, int base, int count, String const & divisor)
	{
		if(count <= 0) return String();
		return functionSubst(wrapping, functionList0(templ, base, count, divisor));
	}

	static String functionListX1(String const & wrapping, String const & templ, int count, String const & divisor)
	{	return functionListX0(wrapping, templ, 0, count, divisor); }
	static String functionListX2(String const & wrapping, String const & templ, int base, int count)
	{	return functionListX0(wrapping, templ, base, count, ", "); }
	static String functionListX3(String const & wrapping, String const & templ, int count)
	{	return functionListX2(wrapping, templ, 0, count); }

	static String functionClassArgs0(String const & prefix, int base, int n, String const & postfix)
	{
		if(!n) return String();
		return prefix + functionList2("class Param%", base, n) + postfix;
	}

	static String functionClassArgs1(int base, int n, String const & postfix)
	{	return functionClassArgs0(String(), base, n, postfix); }
	static String functionClassArgs2(String const & prefix, int base, int n)
	{	return functionClassArgs0(prefix, base, n, String()); }
	static String functionClassArgs3(int base, int n)
	{	return functionClassArgs0(String(), base, n, String()); }
	static String functionClassArgs4(String const & prefix, int n, String const & postfix)
	{	return functionClassArgs0(prefix, 0, n, postfix); }
	static String functionClassArgs5(int n, String const & postfix)
	{	return functionClassArgs0(String(), 0, n, postfix); }
	static String functionClassArgs6(String const & prefix, int n)
	{	return functionClassArgs0(prefix, 0, n, String()); }
	static String functionClassArgs7(int n)
	{	return functionClassArgs0(String(), 0, n, String()); }
	
	static String functionSelArgs0(String const & prefix, int base, int n, String const & postfix)
	{
		if(!n) return String();
		return prefix + functionList2("Param%", base, n) + postfix;
	}

	static String functionSelArgs1(int base, int n, String const & postfix)
	{	return functionSelArgs0(String(), base, n, postfix); }
	static String functionSelArgs2(String const & prefix, int base, int n)
	{	return functionSelArgs0(prefix, base, n, String()); }
	static String functionSelArgs3(int base, int n)
	{	return functionSelArgs0(String(), base, n, String()); }
	static String functionSelArgs4(String const & prefix, int n, String const & postfix)
	{	return functionSelArgs0(prefix, 0, n, postfix); }
	static String functionSelArgs5(int n, String const & postfix)
	{	return functionSelArgs0(String(), 0, n, postfix); }
	static String functionSelArgs6(String const & prefix, int n)
	{	return functionSelArgs0(prefix, 0, n, String()); }
	static String functionSelArgs7(int n)
	{	return functionSelArgs0(String(), 0, n, String()); }

	static String functionFuncArgs0(String const & prefix, int base, int n, String const & postfix)
	{
		if(!n) return String();
		return prefix + functionList2("Param% p%", base, n) + postfix;
	}

	static String functionFuncArgs1(int base, int n, String const & postfix)
	{	return functionFuncArgs0(String(), base, n, postfix); }
	static String functionFuncArgs2(String const & prefix, int base, int n)
	{	return functionFuncArgs0(prefix, base, n, String()); }
	static String functionFuncArgs3(int base, int n)
	{	return functionFuncArgs0(String(), base, n, String()); }
	static String functionFuncArgs4(String const & prefix, int n, String const & postfix)
	{	return functionList0(prefix, 0, n, postfix); }
	static String functionFuncArgs5(int n, String const & postfix)
	{	return functionFuncArgs0(String(), 0, n, postfix); }
	static String functionFuncArgs6(String const & prefix, int n)
	{	return functionFuncArgs0(prefix, 0, n, String()); }
	static String functionFuncArgs7(int n)
	{	return functionFuncArgs0(String(), 0, n, String()); }

	static String functionCallArgs0(String const & prefix, int base, int n, String const & postfix)
	{
		if(!n) return String();
		return prefix + functionList2("p%", base, n) + postfix;
	}

	static String functionCallArgs1(int base, int n, String const & postfix)
	{	return functionCallArgs0(String(), base, n, postfix); }
	static String functionCallArgs2(String const & prefix, int base, int n)
	{	return functionCallArgs0(prefix, base, n, String()); }
	static String functionCallArgs3(int base, int n)
	{	return functionCallArgs0(String(), base, n, String()); }
	static String functionCallArgs4(String const & prefix, int n, String const & postfix)
	{	return functionCallArgs0(prefix, 0, n, postfix); }
	static String functionCallArgs5(int n, String const & postfix)
	{	return functionCallArgs0(String(), 0, n, postfix); }
	static String functionCallArgs6(String const & prefix, int n)
	{	return functionCallArgs0(prefix, 0, n, String()); }
	static String functionCallArgs7(int n)
	{	return functionCallArgs0(String(), 0, n, String()); }
};

#endif //__MCS__VAR_ARG_FUNCTION_COLLECTION__HPP